<template>
  <div class="msg-wrapper" :class="role">
    <div class="msg-content">
      <!-- content 就是 index.vue 中的 streamingText 或 msg.content -->
      <!-- markdownContent 是计算属性, 用来将 content 内容 -->
      <div class="text result-text" v-if="!streaming" v-highlight v-html="markdownContent"></div>
      <!-- 修改这里，使用 white-space: pre-wrap 来保留换行 -->
      <div class="text streaming-text"  v-if="streaming" v-highlight v-html="markdownContent"></div>
    </div>
    <!-- 添加消息操作按钮 -->
    <div class="msg-actions" v-if="role === 'system'">
      <button>👍</button>
      <button>👎</button>
      <button>🔊</button>
      <button>📋</button>
      <button>🔄</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { marked } from 'marked';
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css'; // 使用 VS2015 主题

// 初始化 marked 配置
marked.setOptions({
  headerIds: false,
  mangle: false,
  gfm: false,  // 禁用 GitHub 风格的 markdown
  breaks: true, // 只在真正需要换行时才换行
  paragraph: false, // 不自动添加 p 标签
  highlight: (code, lang) => {
    return hljs.highlightAuto(code).value
  }
})

// 这段代码是Vue组件中的props定义。使用defineProps函数来定义组件的属性，
// 其中role属性的类型被设置为字符串字面量类型，只能是 'user' | 'assistant' | 'system' 中的一个
interface Props {
  role?: 'user' | 'assistant' | 'system';
  content?: string;
  streaming?: boolean;
}

//这段代码是Vue组件中的props默认值设置。使用withDefaults和defineProps组合来定义组件的属性，其中role属性的默认值被设置为'assistant'。
//这意味着如果父组件在使用这个组件时没有传入role属性，那么role的值将默认为'assistant'。
const props = withDefaults(defineProps<Props>(), {
  role: 'assistant',
  content: '',
  streaming: false
});

const markdownContent = computed(() => {
  // 如果内容包含 markdown 特征，才用 marked 处理
  if (/[#*`\-\[\]]/.test(props.content)) {
    return marked(props.content);
  }
  // 普通文本直接返回
  return props.content;
});
</script>

<!-- 
scoped 是 Vue 中的一个样式隔离机制，用于将样式限制在当前组件内，防止样式污染其他组件。

当你在 style 标签上添加 scoped 属性时：

1. Vue 会为组件中的每个元素添加一个唯一的属性（例如： data-v-7a7a37b1 ）
2. 同时将 CSS 选择器改写为带有该属性的选择器（例如： .msg-wrapper[data-v-7a7a37b1] ）
3. 这样确保样式只会应用到当前组件的元素上 

例如 :

<template>
  <div class="msg-wrapper">...</div>
</template>

<style scoped>
  .msg-wrapper { color: red; }
</style>

会被编译为 :

<div class="msg-wrapper" data-v-7a7a37b1>...</div>

<style>
  .msg-wrapper[data-v-7a7a37b1] { color: red; }
</style>

这样做的好处是：

1. 避免样式冲突
2. 提高样式的可维护性
3. 组件样式的模块化
这就是为什么当我们需要修改动态生成的内容样式时，需要使用 :deep() 选择器来突破这个限制。
-->
<style lang="scss" scoped>
.msg-wrapper {
  margin-bottom: 20px;
  display: flex;
  flex-direction: column;

  &.user {
    align-items: flex-end;
    
    .msg-content {
      background: #1e90ff;
      color: white;
      border-radius: 10px 10px 0 10px;
    }
  }

  &.assistant {
    //在CSS Flexbox布局中，如果不指定flex-direction属性，默认值是row（横向），即主轴是水平方向的。元素会按照从左到右的顺序排列。
    //如果要改变为竖向排列，需要显式设置flex-direction: column。
    //并且这里使用了 &, 表示继承了父标签 msg-wrapper 的样式 flex-direction: column;
    //即主轴是竖向的, 这里设置 align-items: flex-start; 表示从轴靠左
    align-items: flex-start;
    
    .msg-content {
      // 这个颜色 回复者 assistant 流式输出时的文本颜色, 至于流式输出完毕后, 由于  <Msg v-if="streaming" role="assistant" :content="streamingText" :streaming="true"></Msg>
      // 不再显示, 所以这个 red 字体颜色也就不会生效, 界面上显示的是 <Msg v-for="(msg, index) in msgList" :role="msg.role" :content="msg.content" :key="index"></Msg>
      // 所以最后的回复者文字是默认黑色而不是红色
      color: black; //这里暂时将红色改为黑色
      border-radius: 10px 10px 10px 0;
    }
  }

  //<div class="msg-wrapper" :class="role"> 
  //没有为元素显式指定color属性时，元素会继承其父元素的颜色，如果父元素也没有指定颜色，最终会使用浏览器的默认颜色，即黑色。
  //在这个例子中，<div class="text" v-else>{{ content }}</div> 的父元素类 .msg-content 没有定义color属性，
  //所以使用了默认的黑色, 如果为 .msg-content 的父元素 <div class="msg-wrapper" :class="role"> 设置 .role 的样式
  //例如 role 是 system 时, 设置 &.system {} 样式字体为绿色, 那么最终消息文本字体也为绿色
  &.system {
    color: black;//这里暂时将绿色改为黑色
  }

  .msg-content {
    max-width: 80%;
    padding: 12px 16px;
    margin-bottom: 8px;
    border-radius: 10px 10px 10px 0;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);

    .text {
      word-break: break-word;
      line-height: 1.5;
    }

    .result-text {
      white-space: pre-wrap; // 添加这个样式以保留换行符
    }

    .streaming-text {
      white-space: pre-wrap; // 添加这个样式以保留换行符
    }
  }

  .msg-actions {
    display: flex;
    gap: 8px;
    padding: 0 4px;

    button {
      background: #f0f2f5;
      border: none;
      padding: 4px 8px;
      border-radius: 4px;
      cursor: pointer;
      transition: background-color 0.2s;

      &:hover {
        background: #e4e6e9;
      }
    }
  }
}

//时我们需要修改子组件或动态生成的内容的样式，这时就需要使用 :deep() 。
/*
这段代码的作用是:
  1. 允许样式穿透到动态生成的 markdown 内容中
  2. 使得我们可以修改 marked 库生成的 HTML 内容的样式
  3. 没有这个选择器，scoped 样式将无法影响到动态生成的内容
*/
:deep() {
  .msg-content {
    background: transparent;

    pre {
      border-radius: 6px;
      overflow: auto;
    }
  }
}
</style>
